﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Google.Protobuf.Examples.AddressBook;
using Google.Protobuf;

namespace testClient
{
    class Program
    {
        public static Dictionary<System.Type, int> protoIds = new Dictionary<System.Type, int>()
		{
			{typeof(MovePosC2S), 1},
			{typeof(UseSkillC2S), 2},
			{typeof(Data), 3},
		};
        public static Dictionary<int, string> idToProto = new Dictionary<int, string>();

        private static TestUDPClient udpClient;

        private static byte[] buffer = new byte[1024];


        private static async void RecvAsync()
        {
            while (true)
            {
                await udpClient.Receive();
            }
            /*
            var stream = client.GetStream();

            while (true)
            {
                var n = 0;

                try
                {
                    n = await stream.ReadAsync(buffer, 0, 1024);
                }
                catch (Exception exp)
                {
                    Console.WriteLine(exp.ToString());
                    return;
                }
                if (n == 0)
                {
                    return;
                }
                else {
					

                    var retStr = Encoding.ASCII.GetString(buffer, 0, n);

                    Console.WriteLine("Receive: " + retStr);
                }

            }
            */
        }




        static void Connect()
        {
            Console.WriteLine("BeforeConnect");

            try
            {
                udpClient = new TestUDPClient();
            }
            catch (Exception exp)
            {
                Console.WriteLine("ConnectFail: " + exp.ToString());
                return;
            }

            RecvAsync();

            Send();
        }

        static void Send()
        {
            while (true)
            {
                var ret = Input();
                if (ret == "close")
                {
                    return;
                }
            }
        }

        private static int curFrameId = 1;
        //s pos x y
        //s skill 123 
        //s ...
        static void SendByte(string[] s)
        {
            udpClient.Send(s);
            /*
            Console.WriteLine("BeforeReceive");
            IMessage msg = null;

            if (s[1] == "pos")
            {
                var x = Convert.ToInt32(s[2]);
                var y = Convert.ToInt32(s[3]);
                var mp = new MovePosC2S()
                {
                    X = x,
                    Y = y,
                };
                msg = mp;
            }
            else if (s[1] == "skill")
            {
                var skillid = Convert.ToInt32(s[2]);
                var sp = new UseSkillC2S()
                {
                    SkillId = skillid,
                };
                msg = sp;
            }
            else {
                var data = new Data()
                {
                    Data_ = s[1],
                };
                msg = data;
            }


            var memStream = new MemoryStream();
            var co = new Google.Protobuf.CodedOutputStream(memStream);
            msg.WriteTo(co);
            co.Flush();
            var data2 = memStream.ToArray();
            var len = (UInt16)data2.Length;

            var byteArray = new byte[2 + 2 + 1 + len];
            var frameId = curFrameId++;
            if (curFrameId >= 256)
            {
                curFrameId = 1;
            }

            var blen = BitConverter.GetBytes(len);

            var typeId = (UInt16)protoIds[msg.GetType()];

            var typeArr = BitConverter.GetBytes(typeId);

            byte fid = (byte)frameId;
            var frameArr = BitConverter.GetBytes(fid);

            //length  typeId frameId pbdata
            Array.Copy(blen, byteArray, 2);
            Array.Copy(typeArr, 0, byteArray, 2, 2);
            Array.Copy(frameArr, 0, byteArray, 4, 1);
            Array.Copy(data2, 0, byteArray, 5, len);

            //byteArray 2 长度  + len 数据
            var stream = client.GetStream();

            try
            {
                stream.Write(byteArray, 0, byteArray.Length);
            }
            catch (Exception exp)
            {
                Console.WriteLine(exp.ToString());
                return;
            }
            */
        }

        static void Close()
        {
            udpClient.Close();
            /*
            client.Close();
            client = null;
            */
        }

        static string Input()
        {
            Console.WriteLine("Please Input:");
            var line = Console.ReadLine();
            var cmds = line.Split(char.Parse(" "));
            var c = cmds[0];
            //c
            //s helloworld
            //close 
            if (c == "c")
            {
                Connect();
            }
            else if (c == "s")
            {
                SendByte(cmds);
            }
            else if (c == "close")
            {
                Close();
            }
            return c;
        }

        public static void Main(string[] args)
        {
            while (true)
            {
                var cmd = Input();
                if (cmd == "close")
                {
                    break;
                }
            }

        }
    }

    public enum ReadState
    {
        HEAD,
        TYPE,
        FRAME_ID,
        BODY,
    } ;

    public class DataStream
    {
        public byte[] buffer = new byte[1024];
        public int readPos = 0;
        public int writePos = 0;

        public void Clear()
        {
            readPos = writePos = 0;
        }
        public UInt16 ReadUInt16()
        {
            return BitConverter.ToUInt16(buffer, 0);
        }
        public byte ReadByte()
        {
            return buffer[0];
        }

        private static Dictionary<int, System.Type> idToProtocols = new Dictionary<int, Type>()
        {
            {1, typeof(MovePosC2S)},
            {2, typeof(UseSkillC2S)},
            {3, typeof(Data)},
        };

        public IMessage ReadData(int typeId)
        {
            var type = idToProtocols[typeId];
            var msg = Activator.CreateInstance(type) as IMessage;

            var ci = new Google.Protobuf.CodedInputStream(buffer, 0, writePos);
            msg.MergeFrom(ci);
            return msg;
        }
    }
    public class MessageProcess
    {
        public ReadState state = ReadState.HEAD;
        public DataStream dataStream = new DataStream();
        private int expectLen = 2;

        private int bodyLength = 0;
        private int typeId = 0;

        public IMessage msgData;
        public void Process(byte[] data, int n)
        {
            var readYet = 0;

            while (n > 0)
            {
                if (state == ReadState.HEAD)
                {
                    if (n >= expectLen)
                    {
                        Array.Copy(data, readYet, dataStream.buffer, dataStream.writePos, expectLen);
                        readYet += expectLen;
                        dataStream.writePos += expectLen;
                        n -= expectLen;

                        bodyLength = dataStream.ReadUInt16();
                        dataStream.Clear();
                        expectLen = 2;

                        state = ReadState.TYPE;
                        Console.WriteLine("BodyLength: " + bodyLength.ToString());
                    }
                    else
                    {
                        Array.Copy(data, readYet, dataStream.buffer, dataStream.writePos, n);
                        readYet += n;
                        dataStream.writePos += n;
                        n -= n;
                    }
                }
                else if (state == ReadState.TYPE)
                {
                    if (n >= expectLen)
                    {
                        Array.Copy(data, readYet, dataStream.buffer, dataStream.writePos, expectLen);
                        readYet += expectLen;
                        dataStream.writePos += expectLen;
                        n -= expectLen;

                        typeId = dataStream.ReadUInt16();
                        dataStream.Clear();

                        expectLen = 1;
                        state = ReadState.FRAME_ID;
                        Console.WriteLine("TypeId: " + typeId);
                    }
                    else
                    {
                        Array.Copy(data, readYet, dataStream.buffer, dataStream.writePos, n);
                        readYet += n;
                        dataStream.writePos += n;
                        n -= n;
                    }
                }
                else if (state == ReadState.FRAME_ID)
                {
                    if (n >= expectLen)
                    {
                        Array.Copy(data, readYet, dataStream.buffer, dataStream.writePos, expectLen);
                        readYet += expectLen;
                        dataStream.writePos += expectLen;
                        n -= expectLen;

                        var data1 = dataStream.ReadByte();
                        Console.WriteLine("FrameId:" + data1.ToString());
                        dataStream.Clear();
                        expectLen = bodyLength;

                        state = ReadState.BODY;
                    }
                    else
                    {
                        Array.Copy(data, readYet, dataStream.buffer, dataStream.writePos, n);
                        readYet += n;
                        dataStream.writePos += n;
                        n -= n;
                    }
                }
                else
                {
                    if (n >= expectLen)
                    {
                        Array.Copy(data, readYet, dataStream.buffer, dataStream.writePos, expectLen);
                        readYet += expectLen;
                        dataStream.writePos += expectLen;
                        n -= expectLen;

                        var data1 = dataStream.ReadData(typeId);
                        msgData = data1;
                        Console.WriteLine(data1.ToString());

                        dataStream.Clear();
                        expectLen = 2;

                        state = ReadState.HEAD;
                    }
                    else
                    {
                        Array.Copy(data, readYet, dataStream.buffer, dataStream.writePos, n);
                        readYet += n;
                        dataStream.writePos += n;
                        n -= n;
                    }
                }
            }
        }
    }
}
